﻿using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyModel;
using Microsoft.Extensions.Logging;
using System;
using System.Linq;
using System.Net;
using System.Reflection;
using System.Threading.Tasks;
namespace Athena
{
    using Codec;
    using Codec.Implementations;
    using Convertible;
    using Convertible.Implementations;
    using Correlation;
    using Correlation.Implementations;
    using Options;
    using RouteAddress;
    using RouteAddress.Implementations;
    using Runtime.Client;
    using Runtime.Client.HealthChecks;
    using Runtime.Client.HealthChecks.Implementations;
    using Runtime.Client.Implementations;
    using Runtime.Client.RouteAddressOverride.Polling;
    using Runtime.Server;
    using Runtime.Server.Implementations;
    using Serialization;
    using Serialization.Implementations;
    public static class AthenaServiceCollectionExtension
    {
        public static IServiceCollection AddAthenaCore(this IServiceCollection serviceCollection, ServiceDescriptor serviceDescriptor)
        {
            return serviceCollection.AddAthenaCore(serviceDescriptor, new AthenaOptions());
        }

        public static IServiceCollection AddAthenaCore(this IServiceCollection serviceCollection, ServiceDescriptor serviceDescriptor, AthenaOptions athenaOptions)
        {
            athenaOptions = athenaOptions ?? new AthenaOptions();
            serviceCollection.AddSingleton(serviceDescriptor);
            serviceCollection.AddSingleton(athenaOptions.HealthCheckOption);
            serviceCollection.AddSingleton(athenaOptions.TransportOption);
            serviceCollection.AddSingleton(athenaOptions.RuntimeServiceOption);
            serviceCollection.AddSingleton(athenaOptions.RouteAddressOption);
            serviceCollection.AddSingleton<IServiceCorrelationIdGenerator, ServiceCorrelationIdGenerator>();
            serviceCollection.AddSingleton<ISerializer<string>, JilJsonSerializer>();
            serviceCollection.AddSingleton<IRpcMessageCodecFactory, JsonRpcMessageCodecFactory>();
            serviceCollection.AddSingleton<ITypeConvertibleProvider, DefaultTypeConvertibleProvider>();
            serviceCollection.AddSingleton<ITypeConvertibleService, DefaultTypeConvertibleService>();
            return serviceCollection;
        }

        public static IServiceCollection AddServiceRuntimeCore(this IServiceCollection serviceCollection)
        {
            serviceCollection.AddSingleton<IServiceEntryFactory, ServiceEntryFactory>();
            serviceCollection.AddSingleton<IServiceDiscover, ServiceDiscoverByAttribute>();
            serviceCollection.AddSingleton<IServiceExecutor, ServiceExecutor>();
            serviceCollection.AddSingleton<IServiceEntrySelector>(provider =>
            {
                var assemblys = DependencyContext.Default.RuntimeLibraries.Where(x => !(x.Name.StartsWith("System") || x.Name.StartsWith("Microsoft"))).SelectMany(y => y.GetDefaultAssemblyNames(DependencyContext.Default).Select(z => Assembly.Load(new AssemblyName(z.Name))));

                var types = assemblys.Where(x => x.IsDynamic == false).SelectMany(x => x.ExportedTypes).ToArray();

                return new ServiceEntrySelector(types, provider.GetRequiredService<IServiceDiscover>());
            });

            return serviceCollection;
        }

        public static IServiceProvider UseService(this IServiceProvider serviceProvider)
        {
            var serivceHost = serviceProvider.GetRequiredService<ISerivceHost>();
            var serviceOption = serviceProvider.GetRequiredService<RuntimeServiceOption>();
            var serviceEntrySelector = serviceProvider.GetRequiredService<IServiceEntrySelector>();
            var serviceMultipleRoute = new ServiceMultipleRoute()
            {
                ServiceDescriptor = serviceProvider.GetRequiredService<ServiceDescriptor>(),
                ServiceRoutes = serviceEntrySelector.SelectAll().Select(x =>
                {
                    return new ServiceRoute()
                    {
                        CorrelationId = x.CorrelationId,
                        Routes = new IPAddressRoute[] {
                            new IPAddressRoute(serviceOption.IP,  serviceOption.Port)
                        }
                    };
                })
            };
            var serviceMultipleRouteManager = serviceProvider.GetRequiredService<IServiceMultipleRouteManager>();
            serviceMultipleRouteManager.SetServiceMultipleRoutesAsync(new ServiceMultipleRoute[] {
                serviceMultipleRoute
            });
            Task.Run(() =>
            {
                serivceHost.StartAsync(new IPEndPoint(IPAddress.Parse(serviceOption.IP), serviceOption.Port));
            }).Wait();
            return serviceProvider;
        }

        public static IServiceCollection AddClientRuntimeCore(this IServiceCollection serviceCollection)
        {
            serviceCollection.AddSingleton<IRpcInvokeService, RpcInvokeService>();
            serviceCollection.AddSingleton<IHealthCheckDispatch, TimerHealthCheckDispatch>();

            return serviceCollection;
        }

        public static IServiceCollection AddRouteCore(this IServiceCollection serviceCollection)
        {
            serviceCollection.AddSingleton<IRouteDescriptorChanger, DefaultRouteDescriptorChanger>();
            serviceCollection.AddSingleton<IRouteResolver, DefaultRouteResolver>();
            return serviceCollection;
        }

        public static IServiceCollection AddFileRouteManager(this IServiceCollection serviceCollection)
        {
            serviceCollection.AddSingleton<IServiceMultipleRouteManager>((provider) =>
            {
                return new FileServiceMultipleRouteManager(
                    provider.GetRequiredService<ServiceDescriptor>(),
                    provider.GetRequiredService<ISerializer<string>>(),
                    provider.GetRequiredService<IRouteDescriptorChanger>(),
                    provider.GetRequiredService<ILogger<FileServiceMultipleRouteManager>>(),
                    provider.GetRequiredService<RouteAddressOption>()
                    );
            });
            return serviceCollection;
        }

        public static IServiceCollection AddRoutePollingSelector(this IServiceCollection serviceCollection)
        {
            serviceCollection.AddSingleton<IRouteSelector, PollingRouteSelector>();
            return serviceCollection;
        }

        public static IServiceCollection AddDefaultClientRuntime(this IServiceCollection serviceCollection)
        {
            serviceCollection.AddClientRuntimeCore();
            serviceCollection.AddRouteCore();
            serviceCollection.AddFileRouteManager();
            serviceCollection.AddRoutePollingSelector();
            return serviceCollection;
        }

        public static IServiceCollection AddAthena(this IServiceCollection serviceCollection, ServiceDescriptor serviceDescriptor, AthenaOptions athenaOptions = null)
        {
            serviceCollection.AddAthenaCore(serviceDescriptor, athenaOptions);
            serviceCollection.AddServiceRuntimeCore();
            serviceCollection.AddDefaultClientRuntime();

            return serviceCollection;
        }


    }
}
